package com.topisv.tms.service.stock.outstorage;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.hibernate.ejb.criteria.predicate.ComparisonPredicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.topisv.tms.entity.AreaGroup;
import com.topisv.tms.entity.Customers;
import com.topisv.tms.entity.User;
import com.topisv.tms.entity.stock.instorage.InstorageBill;
import com.topisv.tms.entity.stock.oustorage.OutstorageBill;
import com.topisv.tms.entity.stock.oustorage.OutstorageBillItem;
import com.topisv.tms.entity.stock.oustorage.OutstorageBox;
import com.topisv.tms.entity.stock.oustorage.OutstorageBoxItem;
import com.topisv.tms.entity.stock.oustorage.OutstorageSendTask;
import com.topisv.tms.repository.AreaGroupDao;
import com.topisv.tms.repository.CustomersDao;
import com.topisv.tms.repository.UserDao;
import com.topisv.tms.repository.stock.instorage.InstorageBillDao;
import com.topisv.tms.repository.stock.outstorage.OutstorageBillDao;
import com.topisv.tms.repository.stock.outstorage.OutstorageBillItemDao;
import com.topisv.tms.repository.stock.outstorage.OutstorageBoxDao;
import com.topisv.tms.repository.stock.outstorage.OutstorageBoxItemDao;
import com.topisv.tms.repository.stock.outstorage.OutstorageSendTaskDao;
import com.topisv.tms.service.vo.invoice.MDParam;
import com.topisv.tms.utils.SystemUtil;
import com.topisv.tms.utils.rule.TMSTransportRule;

/**
 * 出库发货任务_service
 * @author ch
 *
 */
@Component
@Transactional(readOnly = true)
public class OutstorageSendTaskService {
	protected Logger logger = LoggerFactory.getLogger(getClass());
	@Autowired
	private OutstorageSendTaskDao outstorageSendTaskDao;
	@Autowired
	private CustomersDao customersDao;
	@Autowired
	private AreaGroupDao areaGroupDao;
	@Autowired
	private UserDao userDao;
	@Autowired
	private OutstorageBillDao outstoragebilldao;
	@Autowired
	private OutstorageBoxDao outstorageBoxDao;
	@Autowired
	private OutstorageBoxItemDao outstorageBoxItemDao;
	@Autowired
	private InstorageBillDao instorageBillDao;
	@Autowired
	private OutstorageBillItemDao outstorageBillItemDao;
	
	/**
	 * 查找任务
	 * @param param 查询参数
	 * @return
	 */
	public List<OutstorageSendTask> loadTaskByParam(Map<String, Object> param) {
		Specification<OutstorageSendTask> spec = getWhereClause(param);
		List<OutstorageSendTask> workTaskList = this.outstorageSendTaskDao.findAll(spec);
		for (OutstorageSendTask t: workTaskList){
			this.setDetail(t);
		}
		return workTaskList;
	}
	
	/**
	 * 设置属性明细
	 * @param workOrder 
	 */
	private void setDetail(OutstorageSendTask task){
		//customer
		String customerCode=task.getCustomerCode();
		Customers customer=this.customersDao.findByCustomerCode(customerCode);
		task.setCustomer(customer);
		
		//warehouse
		String warehouseCode=task.getWarehouseCode();
		AreaGroup warehouse=this.areaGroupDao.findByGroupCode(warehouseCode);
		task.setWarehouse(warehouse);
		
		//worker
	    String workerUuid=task.getWorkerUuid();
	    User worker=this.userDao.findByJobuuid(workerUuid);
	    task.setWorker(worker);
	    
	    //worker
	    String billCode=task.getBillCode();
	    OutstorageBill bill=this.outstoragebilldao.loadByBillCode(billCode);
	    task.setBill(bill);
	    
	    
	}
	
	/**
	 * 查询条件封装
	 * @param param
	 * @return
	 */
	private Specification<OutstorageSendTask> getWhereClause(final Map<String, Object> param) {
		return new Specification<OutstorageSendTask>(){
			@Override
		    public Predicate toPredicate(Root<OutstorageSendTask> r, CriteriaQuery<?> q, CriteriaBuilder cb) {
		      Predicate pre = cb.conjunction();
		      
		      //customerCodes
		      String customerCodes=(String) param.get("customerCodes");
		      if(StringUtils.isNotBlank(customerCodes)) {
		    	  String[] customerCodeSplit = customerCodes.split(":");
				  ComparisonPredicate[] tempCustomerCode = new ComparisonPredicate[customerCodeSplit.length];
				  int i = 0;
				  for (String _customerCode : customerCodeSplit) {
					 tempCustomerCode[i] = (ComparisonPredicate) cb.equal(r.get("customerCode"), _customerCode);
					 i++;
				  }
				  pre.getExpressions().add(cb.or(tempCustomerCode));
		      }
			  
		      //areagroups
		      String areagroups=(String) param.get("areagroups");
		      if(StringUtils.isNotBlank(areagroups)) {
		    	  String[] areagroupSplit = areagroups.split(":");
				  ComparisonPredicate[] areagroupArray = new ComparisonPredicate[areagroupSplit.length];
				  int j = 0;
				  for (String _areagroup : areagroupSplit) {
						areagroupArray[j] = (ComparisonPredicate) cb.equal(r.get("warehouseCode"), _areagroup);
						j++;
				  }
				  pre.getExpressions().add(cb.or(areagroupArray));
			  }
		      
		      Date fromDate= (Date) param.get("fromDate");
		      if (fromDate != null) {
		        pre.getExpressions().add(cb.greaterThanOrEqualTo(r.<Date>get("createTime"), fromDate));
		      }
		      
		      Date toDate= (Date) param.get("toDate");
		      if (toDate != null) {
		        pre.getExpressions().add(cb.lessThanOrEqualTo(r.<Date>get("createTime"), toDate));
		      }
		      
		      String status=(String) param.get("status"); 
		      if(StringUtils.isNotBlank(status)){
		    	  if(status.equals("myTask")){ //我的发货任务
		    		  pre.getExpressions().add(cb.or(
		    				  cb.equal(r.get("status"),"token"),
		    				  cb.equal(r.get("status"),"doing")
		    				  ));
		    	  }
		    	  else{
		    		  pre.getExpressions().add(cb.equal(r.get("status"),status)); 
		    	  }
			  }
		      
		      String workerUuid=(String) param.get("workerUuid");
		      if(StringUtils.isNotBlank(workerUuid)){
					pre.getExpressions().add(cb.equal(r.get("workerUuid"),workerUuid));
			  }	
		      
		      return pre;
		    }
		  };
       }
	
	/**
	 * 领取任务
	 * @param taskId 任务ID
	 * @param userId 任务执行人
	 * @param taskType 任务类型 （ pick:发货  lampPick:灯光拣选   pkg:包装    send:发货 ）
	 * @throws Exception 
	 */
	@Transactional(readOnly = false,rollbackFor=Exception.class)
	public void takeTask(Long taskId, String jobuuid) throws Exception{
		//修改发货任务状态
		OutstorageSendTask task=this.outstorageSendTaskDao.findOne(taskId);
		task.setWorkerUuid(jobuuid);
		task.setStatus("token"); 
		this.outstorageSendTaskDao.save(task);
		
		//修改出库单状态
		OutstorageBill bill=this.outstoragebilldao.loadByBillCode(task.getBillCode());
		bill.setStatus("4"); //状态 （1.创建出库单 2.领取拣货任务 3.拣货完成  4.领取发货任务  5.发货完成  6.收货确认 )
		this.outstoragebilldao.save(bill);
	}

	/**
	 * 查询
	 * @param taskId
	 * @return
	 */
	public OutstorageSendTask loadTaskById(Long taskId) {
		OutstorageSendTask task=this.outstorageSendTaskDao.findOne(taskId);
		this.setDetail(task);
		return task;
	}

	/**
	 * 完成发货任务
	 * @param taskId
	 * @throws Exception 
	 */
	@Transactional(readOnly = false,rollbackFor=Exception.class)
	public  Map<String,Object> completeTask(Long taskId) throws Exception {
			Map<String,Object> map=new HashMap<String, Object>();
			OutstorageSendTask task=this.outstorageSendTaskDao.findOne(taskId);
			OutstorageBill bill=this.outstoragebilldao.loadByBillCode(task.getBillCode());
			
			//验证任务状态
			if("finish".equals(task.getStatus())){
				map.put("msg", "error_finished");  //已经完成任务
				return map;
			}
			
			//验证货物信息
			Map<String , Integer> boxItemMap=new HashMap<String, Integer>(); //装箱信息
			List<OutstorageBoxItem> boxItemList=this.outstorageBoxItemDao.loadByBillCode(task.getBillCode());
			for (OutstorageBoxItem boxItem : boxItemList) {
				String sku=boxItem.getSku();
				Integer num=boxItem.getNum();
				if(boxItemMap.keySet().contains(sku)){
					boxItemMap.put(sku, boxItemMap.get(sku)+num);
				}else{
					boxItemMap.put(sku, num);
				}
			}
			
			Map<String , Integer> billItemMap=new HashMap<String, Integer>(); //拣货信息
			List<OutstorageBillItem> billItemList=this.outstorageBillItemDao.loadByBillCode(task.getBillCode());
			for (OutstorageBillItem billItem : billItemList) {
				String sku=billItem.getSku();
				Integer num=billItem.getNum();
				if(billItemMap.keySet().contains(sku)){
					billItemMap.put(sku, billItemMap.get(sku)+num);
				}else{
					billItemMap.put(sku, num);
				}
			}
			
			if(boxItemMap.keySet().size()!=billItemMap.keySet().size()){
				map.put("msg", "error_skuNum");  //sku数据不对
				return map;
			}
			
			for (String sku_1 : boxItemMap.keySet()) {
				Integer num_1=boxItemMap.get(sku_1);
				
				if(!billItemMap.containsKey(sku_1)){
					map.put("msg", "error_sku");  //sku异常
					map.put("sku", sku_1);
					return map;
				}
				
				Integer num_2=billItemMap.get(sku_1);
				if(!num_2.equals(num_1)){
					map.put("msg", "error_sku");  //sku异常
					map.put("sku", sku_1);
					return map;
				}
			}
			
			//检验运输信息是否完整
			String driverInfo=bill.getDriverInfo();
			String carInfo=bill.getCarInfo();
			Date outTime=bill.getOutTime();
			if(StringUtils.isBlank(driverInfo) || StringUtils.isBlank(carInfo) || outTime==null ){
				map.put("msg", "error_transInfo");  //运输信息不完整
				return map;
			}
				
			//修改发货任务状态
			task.setStatus("finish");
			this.outstorageSendTaskDao.save(task);
			
			//修改出库单状态
			bill.setStatus("5"); //状态 （1.创建出库单 2.领取拣货任务 3. 拣货完成 4.领取发货任务 5. 发货完成 6.收货确认 )
			List<OutstorageBox> boxList=this.outstorageBoxDao.loadByBillCode(task.getBillCode());
			bill.setBoxNum(boxList.size());
			
			
			//不同的出库类型，不同的处理
			String billType=bill.getBillType(); //出库类型 (allot:调拨出库  sale:销售出库  return:返仓出库)
			if("allot".equals(billType)){ //调拨出库，创建入库单
				 InstorageBill inBill=new InstorageBill();
				 inBill.setCustomerCode(bill.getCustomerCode());
				 inBill.setInwarehouseCode(bill.getWarehouseInCode());
				 inBill.setOutwarehouseCode(bill.getWarehouseOutCode());
				 inBill.setBillType("allot"); //入库单类型  调拨入库
				 inBill.setBoxNum(bill.getBoxNum());
				 inBill.setSkuNum(bill.getSkuNum());
				 inBill.setGoodsNum(bill.getGoodsNum());
				 inBill.setCreateTime(new Date());
				 inBill.setCreaterUuid(bill.getCreaterUuid());
				 inBill.setAboutCode(bill.getBillCode());
				 inBill.setStatus("0");
				 inBill.setErrorFlag("noterror");
				 inBill.setBillCode(TMSTransportRule.generateInStorageBillCode());
				 this.instorageBillDao.save(inBill);
			}
			
			this.outstoragebilldao.save(bill);
			
			map.put("msg", "success");  //发货成功
			
			if("LMB".equals(bill.getCustomerCode())&& "return".equalsIgnoreCase(bill.getBillType())&& bill.getCustomerCode().equalsIgnoreCase(bill.getWarehouseInCode()) &&  null !=bill.getAboutCode())
			{
				
				JSONObject json = new JSONObject();
				
				json.put("outstore_tid", bill.getAboutCode());
				json.put("express_tid", bill.getDriverInfo()+":"+bill.getCarInfo());
				
				JSONArray items = new JSONArray();
				for(Entry<String , Integer> e:billItemMap.entrySet()){
					JSONObject item = new JSONObject();
					item.put("bar_code",e.getKey());
					item.put("outstore_num", e.getValue());
					items.add(item);
				}
				json.put("items", items);
				String data=json.toString();
				String url = MDParam.LMB_URL_OUTSTORE;
				String appkey=MDParam.LMB_APPKEY;
				String md5 = SystemUtil.MD5(appkey + data + MDParam.LMB_KEYWORD);
				String appsecret = SystemUtil.getBASE64(md5);
				
				List<NameValuePair> nvps = new ArrayList<NameValuePair>();
				nvps.add(new BasicNameValuePair("appkey", appkey));
				nvps.add(new BasicNameValuePair("appsecret", appsecret));
				nvps.add(new BasicNameValuePair("data", data));
				logger.info("辣妈帮出库回调：" + "appkey="+appkey + "&appsecret="+ appsecret +"&data=" + data );
				HttpClient httpClient = new DefaultHttpClient();
				org.apache.http.client.methods.HttpPost httpPost = new org.apache.http.client.methods.HttpPost(url);
				httpPost.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8));
				HttpResponse response;
				int errorCount =0;
				while(errorCount <3 ){
					try {
						response = httpClient.execute(httpPost);
						if (response.getStatusLine().getStatusCode() == 200){
							String result=EntityUtils.toString(response.getEntity());
							logger.info("辣妈帮出库回调：" + result);
							JSONObject resJson = JSONObject.fromObject(result);
							if(!"0".equals(resJson.getString("code")))	{
								errorCount ++; 
								logger.error("出库单："+bill.getAboutCode()+"回调返回失败,第"+(errorCount+1)+"次");
							} 	else	{
								break;
							}
						} else {
							errorCount ++; 
							logger.error("出库单："+bill.getAboutCode()+"回调访问失败,第"+(errorCount+1)+"次");
						}
					} catch (Exception e) {
						errorCount ++; 
						logger.error("出库单："+bill.getAboutCode()+"回调异常,第"+(errorCount+1)+"次",e);
					}
				}
			}
			return map;
	}

	/**
	 * 查询箱子
	 * @param taskCode 发货任务编号
	 * @return
	 */
	public List<OutstorageBox> loadByTaskCode(String billCode) {
		List<OutstorageBox> boxList=this.outstorageBoxDao.loadByBillCode(billCode);
		for (OutstorageBox box : boxList) {
			List<OutstorageBoxItem> itemList=this.outstorageBoxItemDao.loadByBoxCode(box.getBoxCode());
			box.setBoxItems(itemList);
		}
		return boxList;
	}


}
